#include "SolutionFactory.h"
#include "../Exception/GA_Exception.h"
#include "../Solution/BitEncoded/BitEncodedSolution.h"
#include "../Solution/PermutationEncoded/PermutationEncodedSolution.h"

int serializeBitEncodedSolution(char buffer[], int maxBufferLen, Solution* solution);
int serializePermutationEncodedSolution(char buffer[], int maxBufferLen, Solution* solution);
Solution* deserializeBitEncodedSolution(const char buffer[], int bufferLen);
Solution* deserializePermutationEncodedSolution(const char buffer[], int bufferLen);

int SolutionFactory::serializeSolution(char buffer[], int maxBufferLen, Solution* solution) {
    SolutionType solutionType = solution->getSerializationIdentifyer();
    int bytesUsed; 
    int headerSize = sizeof(char) + sizeof(int);
    
    if (solutionType == SF_UNSUPPORTED_SOLUTION) {
        throw Serialization_exception();
    }
    
    if (maxBufferLen < headerSize) {
        throw Buffer_undersized_exception();
    }
    
    switch(solutionType) {
        case SF_BIT_ENCODED_SOLUTION:
            //if we have a bit encoded solution:
            bytesUsed = serializeBitEncodedSolution(
                &buffer[headerSize], 
                maxBufferLen - (headerSize), 
                solution);
            break;
        case SF_PERMUTATION_ENCODED_SOLUTION:
            bytesUsed = serializePermutationEncodedSolution(
                &buffer[headerSize], 
                maxBufferLen - (headerSize), 
                solution);
            break;
        default:
            //if we don't know what to do with this type of solution:
            throw Serialization_exception();
    }
    
    buffer[0] = (char) solutionType;
    ((int*) &buffer[1])[0] = bytesUsed;
    return headerSize + bytesUsed;
}

int serializeBitEncodedSolution(char buffer[], int maxBufferLen, Solution* solution) {
    BitEncodedSolution* bes = (BitEncodedSolution*) solution;
    unsigned int solSize = bes->getSize();
    int bytesNeeded = (solSize * sizeof(bool)) + sizeof(unsigned int);
    
    if (bytesNeeded > maxBufferLen) {
        throw Buffer_undersized_exception();
    }
    
    ((unsigned int*) buffer)[0] = solSize;   
    bool* tmp = (bool*) &buffer[sizeof(unsigned int)];
    
    for (unsigned int i = 0; i < solSize; ++i) {
        tmp[i] = bes->getBit(i);
    }
    
    return bytesNeeded;
}

int serializePermutationEncodedSolution(char buffer[], int maxBufferLen, Solution* solution) {
    PermutationEncodedSolution* pes = (PermutationEncodedSolution*) solution;
    unsigned int solSize = pes->size();
    int bytesNeeded = (solSize * sizeof(int));
    
    if (bytesNeeded > maxBufferLen) {
        throw Buffer_undersized_exception();
    }
       
    int* tmp = (int*) &buffer[0];
    
    for (unsigned int i = 0; i < solSize; ++i) {
        tmp[i] = pes->getElement(i);
    }
    
    return bytesNeeded;
}

Solution* SolutionFactory::deserializeSolution(const char buffer[], int bufferLen) {
    int headerSize = sizeof(char) + sizeof(int);
    
    if (bufferLen < headerSize) {
        throw Deserialization_exception();
    }
    
    SolutionType solutionType = (SolutionType) buffer[0];
    int solutionLength = ((int*) &buffer[1])[0];
    
    switch(solutionType) {
        case SF_BIT_ENCODED_SOLUTION:
            //if we have a bit encoded solution:
            return deserializeBitEncodedSolution(
                &buffer[headerSize], 
                solutionLength
            );
            break;
        case SF_PERMUTATION_ENCODED_SOLUTION:
            return deserializePermutationEncodedSolution(
                &buffer[headerSize], 
                solutionLength
            );
            break;
        default:
            //if we don't know what to do with this type of solution:
            throw Serialization_exception();
    }
}

Solution* deserializeBitEncodedSolution(const char buffer[], int bufferLen) {
    BitEncodedSolution* bes = new BitEncodedSolution();
    
    if (bufferLen < 4) {
        throw Deserialization_exception();
    }
    
    unsigned int solSize = ((unsigned int*) buffer)[0];
    bool* tmp = (bool*) &buffer[sizeof(unsigned int)];
    
    for (unsigned int i = 0; i < solSize; ++i) {
        bes->addBit(tmp[i]);
    }
    
    return bes;
}

Solution* deserializePermutationEncodedSolution(const char buffer[], int bufferLen) {
    PermutationEncodedSolution* pes = new PermutationEncodedSolution();
    unsigned int solSize = (bufferLen / sizeof(int));
    int* tmp = (int*) &buffer[0];
    
    for (unsigned int i = 0; i < solSize; ++i) {
        pes->addElement(tmp[i]);
    }
    
    return pes;
}
